namespace SharpTalk.Core.Base
{
    using System;
    using System.Runtime.Serialization;
    using Enums;
    using Exceptions;
    using Interfaces;
    /// <summary>
    /// Abstract user class
    /// </summary>
    [DataContract]
    [Serializable]
    public abstract class UserBase 
        : PropertyChangedBase, IUser, IEquatable<UserBase>
    {
        private Guid _guid;
        private UserStatusEnum _userStatus = UserStatusEnum.NotDefined;
        private string _customStatus;
        private string _firstName;
        private string _lastName;
        private string _nickName;
        private byte[] _image;
        private string _uri;
        private bool _isUpdating = false;
        /// <summary>
        /// Create a new instance of the <see cref="T:SharpTalk.Core.Base.UserBase"/> class.
        /// </summary>
        protected internal UserBase()
        {
            _guid = Guid.NewGuid();
        }
        /// <summary>
        /// Create a new instance of the <see cref="T:SharpTalk.Core.Base.UserBase"/> class.
        /// </summary>
        /// <param name="guid">User guid</param>
        protected internal UserBase(Guid guid)
        {
            _guid = guid;
        }
        /// <summary>
        /// Obtains the Guid
        /// </summary>
        [DataMember(IsRequired = true)]
        public Guid Guid
        {
            get { return _guid; }
            set { _guid = value; }
        }
        /// <summary>
        /// Obtains or defines the user URI
        /// </summary>
        [DataMember]
        public string Uri
        {
            get { return _uri; }
            set { _uri = value; NotifyPropertyChange(()=>Uri); }
        }
        /// <summary>
        /// Obtain or define the user status
        /// </summary>
        [DataMember]
        public virtual UserStatusEnum Status
        {
            get { return _userStatus; }
            set
            {
                if (value == UserStatusEnum.NotDefined)
                    throw new UserStatusNotDefinedException();
                
                _userStatus = value;
                NotifyPropertyChange(() => Status);
                
                if(value == UserStatusEnum.Custom )
                    NotifyPropertyChange(()=>CustomStatus); // notifies CustomStatus changed only if Status is set to Custom
            }
        }
        /// <summary>
        /// Obtain or define a custom status
        /// </summary>
        [DataMember]
        public virtual string CustomStatus
        {
            get { return _customStatus; }
            set 
            { 
                _customStatus = value;
                NotifyPropertyChange(() => CustomStatus);
            }
        }
        /// <summary>
        /// Obtain or define the image
        /// </summary>
        [DataMember]
        public virtual byte[] Image
        {
            get { return _image; }
            set
            {
                _image = value;
                NotifyPropertyChange(()=>Image);
            }
        }
        /// <summary>
        /// Obtain or define the first name of the user
        /// </summary>
        [DataMember]
        public virtual string FirstName
        {
            get { return _firstName; }
            set
            {
                _firstName = value;
                NotifyPropertyChange(() => FirstName, () => FullName);
            }
        }
        /// <summary>
        /// Obtain or define the last name of the user
        /// </summary>
        [DataMember]
        public virtual string LastName
        {
            get { return _lastName; }
            set
            {
                _lastName = value;
                NotifyPropertyChange(() => LastName, () => FullName);
            }
        }
        /// <summary>
        /// Obtain or define the user nickname
        /// </summary>
        [DataMember]
        public virtual string NickName
        {
            get { return _nickName; }
            set
            {
                _nickName = value;
                NotifyPropertyChange(() => NickName, () => FullName);
            }
        }
        /// <summary>
        /// Obtain the fullname of the user
        /// </summary>
        [IgnoreDataMember]
        public virtual string FullName 
        {
            get 
            { 
                return String.Format("{0}{1}{2}", 
                                FirstName, 
                                String.IsNullOrEmpty(NickName) ?  " " : String.Format(" \"{0}\" ", NickName), 
                                LastName).Trim();
            } 
        }
        /// <summary>
        /// Obtains or defines if an update of the user is running
        /// </summary>
        [IgnoreDataMember]
        public bool IsUpdating
        {
            get { return _isUpdating; }
            set { _isUpdating = value; NotifyPropertyChange(()=>IsUpdating); }
        }
        /// <summary>
        /// Indicates whether the current <see cref="T:SharpTalk.Core.Interfaces.IUser"/> is equal to another <see cref="T:SharpTalk.Core.Interfaces.IUser"/>.
        /// </summary>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.</param>
        public virtual bool Equals(IUser other)
        {
            return !ReferenceEquals(null, other) &&
                   Guid.Equals(other.Guid);
        }
        /// <summary>
        /// Indicates whether the current <see cref="T:SharpTalk.Core.Base.UserBase"/> is equal to another <see cref="T:SharpTalk.Core.Base.UserBase"/>.
        /// </summary>
        /// <returns>
        /// true if the current <see cref="T:SharpTalk.Core.Base.UserBase"/> is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        /// <param name="other">An object to compare with this object.</param>
        public bool Equals(UserBase other)
        {
            return !ReferenceEquals(null, other) 
                    && Guid.Equals(other.Guid);
        }
        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. </param><filterpriority>2</filterpriority>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (UserBase)) return false;
            return Equals((UserBase) obj);
        }
        /// <summary>
        /// Serves as a hash function for a particular type. 
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:SharpTalk.Core.Base.UserBase"/>.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        public override int GetHashCode()
        {
            return Guid.GetHashCode();
        }
        /// <summary>
        /// Indicates whether the current <see cref="T:SharpTalk.Core.Base.UserBase"/> is equal to another <see cref="T:SharpTalk.Core.Base.UserBase"/>.
        /// </summary>
        /// <param name="left">An existing instance of the <see cref="T:SharpTalk.Core.Base.UserBase"/> class.</param>
        /// <param name="right">An existing instance of the <see cref="T:SharpTalk.Core.Base.UserBase"/> class.</param>
        /// <returns>True if equals otherwise False.</returns>
        public static bool operator ==(UserBase left, UserBase right)
        {
            return Equals(left, right);
        }
        /// <summary>
        /// Indicates whether the current <see cref="T:SharpTalk.Core.Base.UserBase"/> is not equal to another <see cref="T:SharpTalk.Core.Base.UserBase"/>.
        /// </summary>
        /// <param name="left">An existing instance of the <see cref="T:SharpTalk.Core.Base.UserBase"/> class.</param>
        /// <param name="right">An existing instance of the <see cref="T:SharpTalk.Core.Base.UserBase"/> class.</param>
        /// <returns>True if not equals otherwise False.</returns>
        public static bool operator !=(UserBase left, UserBase right)
        {
            return !Equals(left, right);
        }
    }
}